#include "xworker_double_queue.h"
#include "xthread_manager.h"
#include "xtimer.h"
#include "xdebug.h"

namespace xge{
namespace xge_internal{

  bool WorkerDoubleQueue::PushTask( const TaskPtr& task_ptr ) {
    if(  status() == WorkerQueue::STATUS_LOCKING || status_atomic () ==WorkerQueue::STATUS_RELEASE ) {
      return false;
    }

    task_ptr->set_task_status( Task::TASK_STATUS_WAITING );
    // push the task into the back queue
    queue_sync_lock_.Lock();
    back_queue_->push_back( task_ptr );
    ++back_size_;
    queue_sync_lock_.Unlock();
    // wake up the back thread if we need to do this
    WakeUp();
    return true;
  }

  bool WorkerDoubleQueue::TryPushTask( const TaskPtr& task_ptr ) {
    if(  status() == WorkerQueue::STATUS_LOCKING || status_atomic () ==WorkerQueue::STATUS_RELEASE ) {
      return false;
    }

    if( !queue_sync_lock_.TryLock() ) {
      return false;
    }

    task_ptr->set_task_status( Task::TASK_STATUS_WAITING );
    back_queue_->push_back( task_ptr );
    ++back_size_;
    queue_sync_lock_.Unlock();
    return true;
  }

  size_t WorkerDoubleQueue::PushTask( TaskPtrList* container ) {
    if(  status() == WorkerQueue::STATUS_LOCKING || status_atomic () ==WorkerQueue::STATUS_RELEASE ) {
      return false;
    }
    size_t nums = 0;
    // change the task status using a loop 
    for( TaskPtrList::iterator ib =container->begin() , ie = container->end() ; ib != ie ; ++ib , ++nums ) {
      (*ib)->set_task_status( Task::TASK_STATUS_WAITING );
    }

    queue_sync_lock_.Lock();
    // we use split function to do fast insertion stuff
    back_queue_->splice(back_queue_->begin(),*container,container->begin(),container->end());
    back_size_+=nums;
    queue_sync_lock_.Unlock();
    // do not use container->size() since this may be implemented 
    // as difference( container->begin() , container->end() ); 
    return nums;
  }

  bool WorkerDoubleQueue::WaitWhenEmpty() {
    while( !empty() ) {
      ThreadManager::SwithToAnotherThread();
    }
    return true;
  }

  bool WorkerDoubleQueue::WaitWhenRelease() {
    while( status() != WorkerQueue::STATUS_RELEASE ) {
      ThreadManager::SwithToAnotherThread();
    }

    return true;
  }

  const TripleResult WorkerDoubleQueue::WaitWhenEmpty( int ms ) {
    Timer time;
    Timer::tick_t tick;
    if( !time.BeginTimerCounting() ){
      return TripleResult::kException;
    }
    while( !empty() ) {
      tick += time.TimeDiff();
      if( tick > (size_t)(ms) ) 
        ThreadManager::SwithToAnotherThread();
    }
    return TripleResult::kSucceded;
  }

  const TripleResult WorkerDoubleQueue::WaitWhenRelease( int ms ) {
    Timer time;
    Timer::tick_t tick;
    if( !time.BeginTimerCounting() ){
      return TripleResult::kException;
    }
    while( status() != WorkerQueue::STATUS_RELEASE ) {
      tick+=time.TimeDiff();
      if( tick > (size_t)(ms) ) 
        ThreadManager::SwithToAnotherThread();
    }
    return TripleResult::kSucceded;
  }

  int WorkerDoubleQueue::Loop() {
    // Update the status to working
    set_status( WorkerQueue::STATUS_WORKING );
    // Loop the whole stuff
    while( true ) {
      // just do the batching
      // if it is just start , this does not matter 
      // all the operation below do not need lock
      // since the SwapQueue operation is handle by this loop

      // Running all the front queue task
      TaskPtrList::iterator ib = front_queue_->begin() , ie = front_queue_->end();
      for( ;ib != ie ; ) {
        (*ib)->set_task_status( Task::TASK_STATUS_RUNNING );
        (*ib)->Execute();
        (*ib)->set_task_status( Task::TASK_STATUS_RELEASE );
        --front_size_;
        // pop the task 
        ib = front_queue_->erase(ib);
        // TODO:: Handle quit option here
        if( ProcessQuitOption(ib,ie) ) {
          return WorkerQueue::LOOP_OK_RETURN;
        }
      } 
        
      // Reaching here means we have to swap the queue
      SwapQueue();
      // We have to sleep now 
      if( front_queue_->empty() ) {
        set_status( WorkerQueue::STATUS_SLEEP );
        sleep_lock_.Wait();
        set_status( WorkerQueue::STATUS_WORKING );
        // TODO:: Handle quit option here
        if( ProcessQuitOption(ib,ie) ) {
          return WorkerQueue::LOOP_OK_RETURN;
        }
      }
    }

    // Impossible branch
    UNREACH_BRANCH(_xT("Can not reach outside the task loop!"));
    return WorkerQueue::LOOP_FAILED_RETURN;
  }

  bool WorkerDoubleQueue::ProcessQuitOption( TaskPtrList::iterator& begin , TaskPtrList::iterator& end ) {
    if( quit_option_ == RUNNING ) {
      return false;
    } else if( quit_option_ == QUIT_EMPTY ) {
      for( ; begin != end ; ) {
        (*begin)->set_task_status( Task::TASK_STATUS_RUNNING );
        (*begin)->Execute();
        (*begin)->set_task_status( Task::TASK_STATUS_RELEASE );
        --front_size_;
        begin = front_queue_->erase(begin);
      }
      // Swap the queue
      SwapQueue();
      for( TaskPtrList::iterator ib = front_queue_->begin() , ie = front_queue_->end() ; ib != ie ;  ) {
        (*ib)->set_task_status( Task::TASK_STATUS_RUNNING );
        (*ib)->Execute();
        (*ib)->set_task_status( Task::TASK_STATUS_RELEASE );
        --front_size_;
        ib = front_queue_->erase(ib);
      }
      XASSERT(!empty_);
      empty_ = true;
      set_status_atomic( WorkerQueue::STATUS_RELEASE );
      return true;
    } else {
      set_status_atomic( WorkerQueue::STATUS_RELEASE );
      return true;
    }
  }

}// namespace xge_internal
}// namespace xge